﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using Abp.Authorization;
using Abp.Localization.Sources;
using Hicap.AuthorizationServer.Authorization;
using Hicap.AuthorizationServer.Core;
using Hicap.AuthorizationServer.Core.Common;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Primitives;

namespace Hicap.AuthorizationServer.Web.IdentityServer
{
    public class HicapAuthorizationFilter : IAuthorizationFilter
    {
        private const string NameSpacePrefix = "AppService";

        public void OnAuthorization(AuthorizationFilterContext context)
        {
            if (context.Filters.Any(item => item is IAllowAnonymousFilter))
            {
                return;
            }

            if (!(context.ActionDescriptor is ControllerActionDescriptor))
            {
                return;
            }

            var claims = context.HttpContext.User.Identities.FirstOrDefault()?.Claims;
            context.HttpContext.Request.Headers.TryGetValue("Authorization", out StringValues token);
            Console.WriteLine(token.ToString());

            if (claims == null || !claims.Any())
            {
                context.Result = new UnauthorizedResult();
                return;
            }

            var apiDescriptor = (ControllerActionDescriptor)context.ActionDescriptor;

            var actionHicapAuthorizeAttribute = apiDescriptor.MethodInfo.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            var controllerHicapAuthorizeAttribute = apiDescriptor.MethodInfo.DeclaringType.GetCustomAttributes(true)
                .OfType<HicapAuthorizeAttribute>();

            if (actionHicapAuthorizeAttribute.Any() || controllerHicapAuthorizeAttribute.Any())
            {
                var authObjects = claims.FirstOrDefault(x => x.Type.Equals(ClaimTypes.AuthorizationDecision))?.Value.Split(",");

                if (CheckSpeicaPermission(actionHicapAuthorizeAttribute, controllerHicapAuthorizeAttribute, authObjects))
                    return;

                if (CheckPermession(context, authObjects, apiDescriptor))
                    return;
            }

            return;
        }

        private static bool CheckSpeicaPermission(IEnumerable<HicapAuthorizeAttribute> actionHicapAuthorizeAttribute,
            IEnumerable<HicapAuthorizeAttribute> controllerHicapAuthorizeAttribute, string[] authObjects)
        {
            if (authObjects == null || authObjects.Length == 0)
                return false;

            if (actionHicapAuthorizeAttribute.Union(controllerHicapAuthorizeAttribute)
                .Any(x => authObjects.Any(y => y.Equals(x.AuthCode) && !string.IsNullOrEmpty(x.AuthCode))))
                return true;

            return false;
        }

        private static bool CheckPermession(AuthorizationFilterContext context, string[] authObjects,
            ControllerActionDescriptor apiDescriptor)
        {
            var authObjectName = AuthObjectHelper.ConvertActionNameToAuthObject(apiDescriptor.ActionName);

            if (authObjects == null || authObjects.Length == 0)
                context.Result = new UnauthorizedResult();

            var controllerAuthCode = apiDescriptor.ControllerTypeInfo.FullName
                .Replace(NameSpacePrefix, string.Empty)
                .Replace(AuthorizationServerConsts.SysName, String.Empty)
                .Replace("..", ".")
                .Trim('.');

            if (authObjects.Any(x => x.Equals(AuthorizationServerConsts.SysName)) &&
                authObjects.Any(x => x.Equals(string.Join(".", controllerAuthCode, authObjectName).Trim('.'))))
                return true;
            else
                context.Result = new StatusCodeResult(403);

            return false;
        }
    }
}